<?php

include_once('xml_rpc.class.phps');

function rpc_decode_params($params, &$request_session, &$request_data/*, $debug=false*/) {
	switch(count($params)) {
		case 0:
			$request_session = '';
			$request_data = array();
			break;

		case 1:
			if(is_array($params[0])) {
				$request_session = '';
				$request_data = $params[0];
			} else {
				$request_session = $params[0];
				$request_data = array();
			}
			break;

		default:
			$request_session = $params[0];
			$request_data = $params[1];
			break;
	}

	/*if($debug) {
		echo 'PARAMS: ';
		var_dump($params);
		echo 'SESSION: ';
		var_dump($request_session);
		echo 'DATA: ';
		var_dump($request_data);
	}*/
}

function rpc_error($code, $msg) {
	return array('type' => 'error', 'data' => array($code, $msg));
}

function rpc_data(&$data) {
	return array('type' => 'data', 'data' => $data);
}

function rpc_is_error($result) {
	return $result['type'] == 'error';
}

function rpc_handle_login($request_data) {
	global $cfg, $rx;

	if(!isset($request_data['username']) || !isset($request_data['domain'])) {
		return rpc_error(4001, 'You must specify a username and domain with system.login');
	}

	$username = $request_data['username'];
	$userdomain = $request_data['domain'];
	if(isset($request_data['password'])) {
		$salt = '';
		$password = md5($request_data['password']);
	} elseif(isset($request_data['time']) && isset($request_data['hash'])){
		$salt = $request_data['time'];
		$time = XMLRPC_convert_iso8601_to_timestamp($salt);
		if($time > gmtime() || $time+(60*5/*5min*/) < gmtime()) {
			return rpc_error(4003, 'Time passed to system.login has expired, make sure your clock is set correctly and that you\'re sending UTC.');
		}
		$password = $request_data['hash'];
	} else {
		return rpc_error(4002, 'system.login requires either a password or time and hash');
	}

	if (!eregi($rx['user'], $username))
		$username = '_invalid';

	// get remote IP address
	$ip = get_remote_ip();

	// Check for failed logins within the last 24 hrs from this IP
	$fl = mysql_query("SELECT count(id) FROM log WHERE remote_ip = \"$ip\" AND log_msg = \"login failed\" AND time > DATE_SUB(NOW(), INTERVAL 1 DAY)");
	list($fail_count) = mysql_fetch_row($fl);
	if ($fail_count < 15 && $fail_count > 2) {
		sleep($fail_count);
	} elseif ($fail_count >= 15)  {
		$password = '';
	}

	//Get user data
	$query = 'SELECT d.id AS domainid, u.type AS type, DECODE(password,"'.$cfg['key'].'") AS password
	          FROM domains d INNER JOIN users u ON (d.id = u.id)
	          WHERE domain = "'.$userdomain.'" AND username = "'.$username.'"
	          LIMIT 1';
	$res = mysql_query($query);
	$data = mysql_fetch_assoc($res);

	// If the user is valid
	if (!empty($data) && md5($data['password'].$salt) == $password && $data['type']!='demo') {
		$domainid = $data['domainid'];
		webcp_log(1,"",$username." ($domainid)","login successful",$_SERVER['REMOTE_ADDR']);

		do {
			$webcp_tag = '+'.strtolower(make_salt(31)); //mysql is case insensitive
			$dbp = mysql_query("SELECT username FROM users WHERE webcp_tag='$webcp_tag' LIMIT 1");
		} while (mysql_num_rows($dbp)!=0);
		mysql_query("UPDATE users SET webcp_tag='$webcp_tag', remote_addr='".$_SERVER['REMOTE_ADDR']."', timeout='".(time() + 60*5)."' WHERE username='$username' AND id = '$domainid'");
	} else {
		if(!empty($data)) {
			webcp_log(1,"",$username." ($domainid)","login failed",$_SERVER['REMOTE_ADDR']);
		}

		return rpc_error(4004, 'Invalid login or demo user passed to system.login');
	}

	//XMLRPC_response(XMLRPC_prepare($webcp_tag));

	return rpc_data($webcp_tag);
}

function rpc_verify_login($request_session) {
	global $userdata;

	$ip = get_remote_ip();

	$dbp = mysql_query("SELECT timeout, level, type, remote_addr, suspend, ip_restrict, username, id FROM users WHERE webcp_tag='$request_session'");
	$tmpdata = mysql_fetch_assoc($dbp);

	// fail if the tag is not recognized
	if (!is_array($tmpdata) || empty($request_session)) {
		return rpc_error(3000, 'Invalid session, please login again');
	}
	// fail if the ID expired (Automated Unique ID timeout for security)
	if (time() > $tmpdata['timeout']) {
		return rpc_error(3001, 'Session timed out, please login again');
	}

	//this shouldn't happen since we don't allowed them to login anyway
	if($tmpdata['type'] == 'demo') {
		return rpc_error(4004, 'Invalid login or demo user passed to system.login');
	}

	// fail if Remote user's IP is different than the IP recorded in DB (and not a demo user)
	if ($tmpdata['remote_addr'] != $ip) {
		return rpc_error(4006, 'Session IP doesn\'t match your IP ('.$ip.')');
	}

	// fail if user is suspended
	if ($tmpdata['suspend'] == "true") {
		return rpc_error(4007, 'This user has been suspended');
	}

	// fail if Remote user's IP is different than ip restriction (if on)
	if ($tmpdata['ip_restrict'] AND !ereg("^".$tmpdata['ip_restrict'],$ip)) {
		return rpc_error(4008, 'IP restriction doesn\'t match your IP ('.$ip.')');
	}

	/*$userdata = array('username' => $tmpdata['username'],
	                  'id' => $tmpdata['id'],
					  'level' => $tmpdata['level']);*/
	$userdata = fetchdata('*', 'user', array($tmpdata['username'], $tmpdata['id']));

	return rpc_data($userdata);
}

function rpc_merge_data(&$data, &$passed_data) {
	if(is_array($data)) {
		if(is_array($passed_data))
			array_concat($data, $passed_data, false);
		else
			$data[] = $passed_data;
	}
}

function rpc_init_vars($request_data) {
	global $userdata, $personaldata, $domaindata, $resellerdata, $cfg;

	if(!empty($personaldata) && !empty($domaindata) && !empty($resellerdata)
	   && !empty($request_data['username']) && $personaldata['username']==$request_data['username']
	   && !empty($request_data['id']) && $personaldata['id']==$request_data['id']) {
		return; //already initialized with the same values
	}

	$username = $userdata['username'];
	$domainid = $userdata['id'];

	if($userdata['level'] <= $cfg['un_change_level']) {
		switch($userdata['level']) {
			case USER_LEVEL_ROOT:
			case USER_LEVEL_SERVER:
				if(isset($request_data['username']))
					$username = $request_data['username'];
				if(isset($request_data['id']))
					$domainid = $request_data['id'];
				break;

			case USER_LEVEL_RESELLER:
				if(isset($request_data['username']))
					$username = $request_data['username'];
				//only allow to change to domains of the same reseller
				if(isset($request_data['id']) && substr($domainid, 0, 5)==substr($request_data['id'], 0, 5))
					$domainid = $request_data['id'];
				break;

			case USER_LEVEL_DOMAIN:
				//Don't allow to switch domain, only user
				if(isset($request_data['username']))
					$username = $request_data['username'];
				break;

			//Lower levels can't change anything
		}
	}

	/*echo 'USERNAME, ID: ';
	var_dump($username, $domainid);*/

	$personaldata = fetchdata("*", "user", array($username, $domainid));
	if($personaldata==null) {
		//fallback
		$personaldata = $userdata;
	} elseif($personaldata['level']<$userdata['level']) {
		//Can't increase permissions, reset
		$username = $userdata['username'];
		$domainid = $userdata['id'];
		$personaldata = fetchdata("*", "user", array($username, $domainid));
	}
	$domaindata = fetchdata(($userdata['level'] >= USER_LEVEL_USER)?'host,domain':'*', 'domain', $domainid);
	$resellerdata = fetchdata(($userdata['level'] >= USER_LEVEL_DOMAIN)?'name':'*', 'reseller', $domainid);

	if((isset($request_data['username']) && $request_data['username'] != $username)
	   || (isset($request_data['id']) && $request_data['id'] != $domainid))
		return false;

	return true;
}

function rpc_handle_method($request_method, $request_data, $request_session, $passed_data=array()) {
	global $logged_id;

	$request_method = /*strtolower(*/str_replace('.', ':', $request_method)/*)*/;

	switch($request_method) {
		case 'system:login':
			$result = rpc_handle_login($request_data);
			if(!rpc_is_error($result)) {
				$request_session = $result['data'];

				$result2 = rpc_verify_login($request_session);
				if(rpc_is_error($result2)) {
					return $result2;
				} else
					$logged_id = true;
			}

			return $result;

		case 'system:multicall':
			$results = array();
			if(!is_array($request_data)) {
				return rpc_error(4009, 'Invalid arguments to system.multicall');
			}
			foreach($request_data as $request) {
				$request_method2 = $request[0];
				$request_session2 = '';
				$request_data2 = array();

				rpc_decode_params($request[1], $request_session2, $request_data2);

				//rpc_merge_data($request_data2, $passed_data);

				$result = rpc_handle_method($request_method2, $request_data2, $request_session, $passed_data);
				//$result = rpc_handle_method($request_method2, $request_data, $request_session);
				if(rpc_is_error($result)) {
					$results[] = array('faultCode' => $result['data'][0], 'faultString' => $result['data'][1]);
				} else {
					$results[] = array($result['data']);
				}
			}
			return $results;

		case 'system:cascadecall':
			$results = array();
			if(!is_array($request_data)) {
				return rpc_error(4010, 'Invalid arguments to system.cascadecall');
			}
			foreach($request_data as $request) {
				$request_method2 = $request[0];
				$request_session2 = '';
				$request_data2 = array();

				/*echo 'cascadecall: '."\n";
				var_dump($request);*/

				rpc_decode_params($request[1], $request_session2, $request_data2/*, true*/);

				/*var_dump($request_session2);
				var_dump($request_data2);

				echo 'end;'."\n";*/

				//rpc_merge_data($request_data2, $passed_data);

				//merge result from previous method with request data
				if(!empty($results))
					list($passed_data2) = end($results);
				else
					$passed_data2 = $passed_data;

				$result = rpc_handle_method($request_method2, $request_data2, $request_session, $passed_data2);
				if(rpc_is_error($result)) {
					$results[] = array('faultCode' => $result['data'][0], 'faultString' => $result['data'][1]);
					break; //abort on error
				} else {
					$results[] = array($result['data']);
				}
			}
			return rpc_data($results);

		default:
			//require login for all non-system methods
			if(substr($request_method, 0, 7)!='system:' && !$logged_in) {
				$result = rpc_verify_login($request_session);
				if(rpc_is_error($result)) {
					return $result;
				} else
					$logged_in = true;
			}

			/*echo 'methodCall: '.$request_method."\n";
			var_dump($request_data);
			var_dump($passed_data);*/

			rpc_merge_data($request_data, $passed_data);

			//var_dump($request_data);

			$data = array('method' => $request_method,
			              'session' => $request_session,
						  'data' => $request_data,
						  'result' => null);

			call_hook('ext:'.$request_method, $data);

			if($data['result']===null) {
				$data['result'] = rpc_error(4005, 'The method "'.str_replace(':', '.', $request_method).'" is not available');
			}

			//echo 'end;'."\n";

			return $data['result'];
	}
}

?>